package com.example.everying.framework.websocket.service;

import com.alibaba.fastjson2.JSONObject;
import com.example.everying.framework.websocket.factory.MessageHandlerFactory;
import com.example.everying.framework.websocket.model.WebSocketClient;
import com.example.everying.framework.websocket.utils.WebSocketRedisUtil;
import com.example.everying.model.entity.system.SysNotification;
import com.example.everying.model.entity.user.UserEntity;
import com.example.everying.framework.websocket.handler.MessageHandler;
import com.example.everying.service.chat.impl.RedDotService;
import com.example.everying.service.user.UserService;
import com.example.everying.utils.JwtUtils;
import io.swagger.annotations.Api;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.CrossOrigin;

import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author szy
 * @date 2025/05/14
 */

@Slf4j
@Setter
@Component
@Api(value = "websocket聊天服务")
@CrossOrigin(origins = "*")
@ServerEndpoint(value = "/websocket/chat")
public class WebSocketChatService {

    private static ConcurrentHashMap<String, WebSocketClient> webSocketMap = new ConcurrentHashMap<>(); // 存放所有在线连接客户端
    private static ConcurrentHashMap<String, String> userIdMap = new ConcurrentHashMap<>();// sessionId / userId

    private static UserService userService;
    private static WebSocketRedisUtil webSocketRedisUtil;
    private static RedDotService redDotService;

    @Autowired
    private void setService(UserService userService,
                            WebSocketRedisUtil webSocketRedisUtil,
                            RedDotService redDotService) {
        WebSocketChatService.userService = userService;
        WebSocketChatService.webSocketRedisUtil = webSocketRedisUtil;
        WebSocketChatService.redDotService = redDotService;
    }


    /**
     * 客户端与服务端连接成功
     *
     * @param session 会话
     */
    @OnOpen
    public void onOpen(Session session) throws IOException {
        log.info("有人连接");
        WebSocketClient client = new WebSocketClient();
        client.setSession(session);
        client.setUri(session.getRequestURI().toString());
        // 通过携带的token解析出用户
        String token = session.getRequestParameterMap().get("token").get(0);
        log.info("用户token: {}", token);
        // 判断token是否有效
        boolean result = JwtUtils.verify(token);
        if (!result) {
            log.info("token无效");
            return;
        }
        String id = JwtUtils.tokenParse(token);
        log.info("用户id: {}", id);
        UserEntity user = userService.getUserById(id);
        if (user == null) {
            log.info("用户不存在");
            if (session.isOpen()) {
                session.close(); // 主动关闭连接
            }
            return;
        }
        webSocketMap.put(user.getId(), client);
        userIdMap.put(session.getId(), user.getId());
        // 存入redis
        webSocketRedisUtil.updateUserActivity(user.getId());
        log.info("用户{}连接成功！{}", user.getPhone(), session);
    }

    /**
     * 客户端与服务端连接关闭
     */
    @OnClose
    public void onClose(Session session) {
        log.info("连接关闭！");
        // 打印一下时间
//        log.info("关闭的当前时间: {}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        String sessionId = session.getId();
        String userId = userIdMap.get(sessionId);
        log.info("WebSocket 连接关闭，用户ID: {}, Session ID: {}", userId, sessionId);
        if (userId != null) {
            webSocketMap.remove(userId);
        }
        userIdMap.remove(sessionId);

        try {
            if (session.isOpen()) {
                session.close(); // 主动关闭连接
                log.info("WebSocket 连接已主动关闭，用户ID: {}", userId);
            }
        } catch (IOException e) {
            log.error("关闭 WebSocket 连接时发生异常", e);
        }

        webSocketRedisUtil.cleanUserRecord(userId);
    }

    public void onClose(String userId) {
        // ... existing code ...
        log.info("心跳超时，连接关闭！{}", userId);

        // 检查 userId 是否存在于 webSocketMap 中
        if (webSocketMap.containsKey(userId)) {
            Session session = webSocketMap.get(userId).getSession();
            String sessionId = userIdMap.get(userId);

            try {
                if (session != null && session.isOpen()) {
                    session.close(); // 主动关闭连接
                }
            } catch (IOException e) {
                log.error("关闭 WebSocket 连接时发生异常", e);
            } finally {
                // 确保无论是否抛出异常，都移除映射关系
                webSocketMap.remove(userId);
                if (sessionId != null) {
                    userIdMap.remove(sessionId);
                }
            }
        } else {
            log.warn("尝试关闭不存在的 WebSocket 连接，用户ID: {}", userId);
        }
    }


    /**
     * 客户端与服务端连接异常
     *
     * @param error
     * @param session
     */
    @OnError
    public void onError(Throwable error, Session session) {
        log.error("发生错误：", error);
        String sessionId = session.getId();
        String userId = userIdMap.get(sessionId);
        if (userId != null) {
            webSocketMap.remove(userId);
        }
        userIdMap.remove(sessionId);
        webSocketRedisUtil.cleanUserRecord(userId);
    }

    /**
     * 客户端向服务端发送消息
     *
     * @param messageStr 消息
     */
    @OnMessage
    public void onMsg(Session session, String messageStr) {
        JSONObject message = JSONObject.parseObject(messageStr);
        // 排除心跳消息
        if (message != null && "heartbeat".equals(message.getString("type"))) {
            log.debug(" 忽略心跳消息");
        } else {
            log.info("收到消息: {}", message);
        }
//        log.info("收到消息: {}", message);

        handleMessage(message, session);
    }


    /**
     * 向指定客户端发送消息
     *
     * @param message
     */
    public static void sendMessageToUserId(String userId, String message) {
        try {
            log.info("准备发消息,用户ID:{}", userId);
            WebSocketClient webSocketClient = webSocketMap.get(userId);
            if (webSocketClient != null) {
                log.info("发送的消息是:{}", message);
                webSocketClient.getSession().getBasicRemote().sendText(message);
            }
        } catch (IOException e) {
            log.info("发送失败->msg:{}", e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 向指定客户端发送消息  心跳检测单独发，不打印日志
     *
     * @param message
     */
    public static void sendHeartbeatMessageToUserId(String userId, String message) {
        try {
            WebSocketClient webSocketClient = webSocketMap.get(userId);
            if (webSocketClient != null) {
                webSocketClient.getSession().getBasicRemote().sendText(message);
            } else {
                log.info("用户不在线,用户ID:{}", userId);
            }
            // 单独处理一下等30秒主动清除97962507050937用户的缓存
//            scheduleRemoveUserWebSocketCache("97962507050937", 30);
        } catch (IOException e) {
            log.info("发送失败->msg:{}", e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 定时清除指定用户的WebSocket缓存（测试用）
     *
     * @param userId       要清除缓存的用户ID
     * @param delaySeconds 延迟秒数
     */
    public static void scheduleRemoveUserWebSocketCache(String userId, long delaySeconds) {
        if (userId == null || userId.isEmpty()) {
            System.out.println(" 用户ID不能为空");
            return;
        }

        System.out.println(" 已设置" + delaySeconds + "秒后清除用户 " + userId + " 的WebSocket缓存");

        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                webSocketRedisUtil.cleanUserRecord(userId);
//                onClose(userId);
            }
        }, delaySeconds * 1000); // 转换为毫秒
    }

    public static String getUserId(Session session) {
        return userIdMap.get(session.getId());
    }

    public static boolean isOnline(String userId) {
        return userIdMap.containsValue(userId);
    }

    /**
     * 处理消息的方法
     *
     * @param result  反序列化后的消息结果
     * @param session 当前会话
     */
    private static void handleMessage(JSONObject result, Session session) {
        try {
            String type = result.getString("type");
            MessageHandler handler = MessageHandlerFactory.getHandler(type);
            handler.handle(session, result);
        } catch (RuntimeException e) {
            log.error("处理消息时发生异常: {}  type :{}", e.getMessage(), result.getString("type"));
//            log.error("未找到消息处理程序: {}", e.getMessage());
        }
    }

    /**
     * 向本节点的所有连接广播消息
     */
    public static void localBroadcast(String message) {
        webSocketMap.forEach((userId, client) -> {
            try {
                if (client.getSession().isOpen()) {
                    client.getSession().getBasicRemote().sendText(message);
                    redDotService.sendRedDot(userId);
                }
            } catch (IOException e) {
                log.error(" 发送消息到用户 {} 失败: {}", userId, e.getMessage());
                // 清理无效连接
                webSocketMap.remove(userId);
                userIdMap.values().removeIf(v -> v.equals(userId));
            }
        });
    }

    /**
     * 发送官方通知给用户
     *
     * @param userId
     * @param jsonString
     * @param notification
     */
    public static void sendMessageNotice(String userId, JSONObject jsonString, SysNotification notification) {
        try {
            //发送WebSocket通知
            WebSocketClient webSocketClient = webSocketMap.get(userId);
            if (webSocketClient != null && webSocketClient.getSession().isOpen()) {
                webSocketClient.getSession().getBasicRemote().sendText(jsonString.toJSONString());
                log.info("系统通知发送成功: {}", jsonString.get("content"));
            } else {
                //用户不在线时存储离线通知
                webSocketRedisUtil.saveOfflineNotification(userId, notification);
                log.info("用户{}不在线，通知已存入离线队列", userId);
            }
        } catch (IOException e) {
            log.info("发送失败->msg:{}", e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
    }


}

